package zom.zenjava.zendeploy.util;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;

public class FileDownloader
{
    private static final int DEFAULT_BUFFER_SIZE = 1024;

    private URL remoteUrl;
    private File localFile;
    private FileDownloadMonitor monitor;
    private int bufferSize;
    private boolean cancelled;
    
    public FileDownloader(URL remoteUrl, File localFile)
    {
        this(remoteUrl, localFile, null, DEFAULT_BUFFER_SIZE);
    }

    public FileDownloader(URL remoteUrl, File localFile, FileDownloadMonitor monitor)
    {
        this(remoteUrl, localFile, monitor, DEFAULT_BUFFER_SIZE);
    }

    public FileDownloader(URL remoteUrl, File localFile, FileDownloadMonitor monitor, int bufferSize)
    {
        this.remoteUrl = remoteUrl;
        this.localFile = localFile;
        this.monitor = monitor;
        this.bufferSize = bufferSize;
        this.cancelled = false;
    }

    public URL getRemoteUrl()
    {
        return remoteUrl;
    }

    public File getLocalFile()
    {
        return localFile;
    }

    public FileDownloadMonitor getMonitor()
    {
        return monitor;
    }

    public int getBufferSize()
    {
        return bufferSize;
    }

    public boolean isCancelled()
    {
        return cancelled;
    }

    public synchronized void cancel()
    {
        this.cancelled = true;
    }

    public void download() throws IOException
    {
        BufferedInputStream reader = null;
        BufferedOutputStream writer = null;
        try
        {
            URLConnection connection = remoteUrl.openConnection();

            checkCancelled();

            int totalBytesToRead = -1;
            List values = connection.getHeaderFields().get("content-Length");
            if (values != null && !values.isEmpty())
            {
                String fileSizeAsString = (String) values.get(0);
                if (fileSizeAsString != null)
                {
                    try
                    {
                        totalBytesToRead = Integer.parseInt(fileSizeAsString);
                    }
                    catch (NumberFormatException e)
                    {
                        // unable to parse length, just leave it as -1 (indeterminate)
                    }
                }
            }

            reader = new BufferedInputStream( connection.getInputStream() );
            writer = new BufferedOutputStream( new FileOutputStream(localFile ) );

            checkCancelled();

            byte[] buffer = new byte[bufferSize];
            int totalBytesReadSoFar = 0;
            int bytesRead;
            while ((bytesRead = reader.read(buffer)) > 0)
            {
                writer.write(buffer, 0, bytesRead);
                totalBytesReadSoFar += bytesRead;
                if (monitor != null)
                {
                    monitor.fileDownloadUpdated(new FileDownloadUpdate(this, totalBytesReadSoFar, totalBytesToRead));
                }

                checkCancelled();
            }
        }
        finally
        {
            if (reader != null)
            {
                try
                {
                    reader.close();
                }
                catch (IOException ioe)
                {
                    ioe.printStackTrace();
                }
            }

            if (writer != null)
            {
                try
                {
                    writer.close();
                }
                catch (IOException ioe)
                {
                    ioe.printStackTrace();
                }
            }
        }
    }

    private void checkCancelled() throws DownloadCancelledException
    {
        synchronized (this)
        {
            if (cancelled) 
            {
                throw new DownloadCancelledException();
            }
        }
    }
}
